package org.iabako.server.serviceimpl.user;

import org.hibernate.Hibernate;
import org.iabako.server.conf.AppConf;
import org.iabako.server.dao.JpaDAO;
import org.iabako.server.dao.user.EnterpriseDAO;
import org.iabako.server.dao.user.UserDAO;
import org.iabako.server.dao.user.UserPayPalTokenDAO;
import org.iabako.server.mail.IabakoMailHelper;
import org.iabako.server.mail.IabakoMailSender;
import org.iabako.server.serviceimpl.business.AbstractServiceImpl;
import org.iabako.server.tools.BundleMessages;
import org.iabako.shared.entity.business.Notification;
import org.iabako.shared.entity.enumeration.IabakoPackage;
import org.iabako.shared.entity.user.Enterprise;
import org.iabako.shared.entity.user.EnterpriseCustomInfo;
import org.iabako.shared.entity.user.User;
import org.iabako.shared.entity.user.UserPayPalToken;
import org.iabako.shared.exception.IabakoUniqueConstraintException;
import org.iabako.shared.iservice.user.SignUpService;
import org.iabako.shared.iservice.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpSession;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import java.util.Random;

/**
 * Created by jose on 08/12/14.
 */

@Service("signUpService")
public class SignUpServiceImpl extends AbstractServiceImpl implements SignUpService {

    @Autowired
    UserDAO userDAO;
    @Autowired
    UserPayPalTokenDAO userPayPalTokenDAO;
    @Autowired
    EnterpriseDAO enterpriseDAO;
    @Autowired
    UserService userService;
    @Autowired
    IabakoMailSender iabakoMailSender;
    @Autowired
    private AppConf appConf;
    @Autowired
    BundleMessages messages;

    static private final Random random = new Random();

    @Override
    protected JpaDAO getDAO() {
        return userDAO;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Enterprise saveEnterprise(Enterprise e) {
        if (e.getId() != null && e.getLogo() == null && e.getNotificationList().isEmpty()) {
            //May be it comes from Gwt client world
            //We must charge Enterprise from DB because of transient attributes Logo and Notifications list (with orphan removal)
            Enterprise enterprise = enterpriseDAO.findById(e.getId());
            //Modifiable by client-side attributes
            enterprise.setName(e.getName());
            enterprise.setEmail(e.getEmail());
            enterprise.setAddress(e.getAddress());
            enterprise.setPhoneNumber(e.getPhoneNumber());
            enterprise.setWebSite(e.getWebSite());
            enterprise.setContentTypeLogo(e.getContentTypeLogo());
            enterprise.setLanguage(e.getLanguage());
            enterprise.setInvoiceNote(e.getInvoiceNote());
            if (e.getCustomInfoList() != null) {
                //It comes from EnterpriseCustomInfoForm (GWT client world)
                enterprise.setCustomInfoList(e.getCustomInfoList());
            }
            for (Notification n : enterprise.getNotificationList()) {
                Hibernate.initialize(n);
                Hibernate.initialize(n.getHiddenUsers());
            }
            e = enterprise;
        }

        Enterprise enterpriseInServerSide = getEnterpriseFromSessionUser();

        if (e.getContentTypeLogo() == null) {
            //May be Enterprise's logo has been deleted in client-side
            e.setLogo(null);

        } else if (enterpriseInServerSide != null && e.getLogo() == null && enterpriseInServerSide.getLogo() != null && e != enterpriseInServerSide) {
            //If parameter's enterprise logo is null, may be it comes from GWT client side.
            //We should search if it is also null in Enterprise session scoped bean (server side)
            e.setLogo(enterpriseInServerSide.getLogo());
            e.setContentTypeLogo(enterpriseInServerSide.getContentTypeLogo());
        }

        if (e.getLanguage() == null) {
            e.setLanguage(messages.getLanguage());
        }

        //In sign up process there's no user in session yet
        if (getSessionUser() != null) {
            getSessionUser().setEnterprise(e);
        }

        //In sign up process Enterprise's email is not yet provided
        if (e.getEmail() == null) {
            e.setEmail(messages.getLabel("sign_up_form_enterprise_email_to_complete"));
        }

        super.save(e);

        //Transient fields
        e.setChildrenEnterprises(enterpriseDAO.getChildrenEnterprises(e));
        e.setAllRelatedEnterprise(enterpriseDAO.getAllRelatedEnterprises(e));
        e.setUsersNumber(enterpriseDAO.getUsersNumber(e));

        for (Enterprise relatedEnterprise : e.getAllRelatedEnterprises()) {
            if (!e.equals(relatedEnterprise)) {
                saveChangesInRelatedEnterprises(e, relatedEnterprise);
            }
        }

        return e;
    }

    private void saveChangesInRelatedEnterprises(Enterprise e, Enterprise relatedEnterprise) {
        //We refresh object because of lazy relationships
        relatedEnterprise = enterpriseDAO.findById(relatedEnterprise.getId());
        relatedEnterprise.setName(e.getName());
        relatedEnterprise.setEmail(e.getEmail());
        relatedEnterprise.setAddress(e.getAddress());
        relatedEnterprise.setPhoneNumber(e.getPhoneNumber());
        relatedEnterprise.setWebSite(e.getWebSite());
        relatedEnterprise.setLanguage(e.getLanguage());
        super.save(relatedEnterprise);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Enterprise saveEnterprise(Enterprise enterprise, List<EnterpriseCustomInfo> customInfoList) {
        enterpriseDAO.mergeCustomList(enterprise, customInfoList);
        return saveEnterprise(enterprise);
    }

    public List<EnterpriseCustomInfo> getCustomInfoList() {
        return enterpriseDAO.getCustomInfoList();
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public User saveUser(User user) throws IabakoUniqueConstraintException {

        String emailFromDB = null;

        if (user.getId() != null) {
            User userFromDB = (User) userService.getById(user.getId());
            emailFromDB = userFromDB.getEmail();
        }

        //Enterprise's email persist
        user.getEnterprise().setEmail(user.getEmail());
        saveEnterprise(user.getEnterprise());

        user = userService.save(user);

        if (!user.getEmail().equals(emailFromDB)) {
            //First time or email modification in signUpForm

            HttpSession session = (((ServletRequestAttributes) RequestContextHolder.
                    currentRequestAttributes()).getRequest()).getSession();

            Integer keyCode = random.nextInt((999999 - 100000) + 1) + 100000;
            session.setAttribute(user.getEmail(), keyCode);

            IabakoMailHelper iabakoMailHelper = new IabakoMailHelper(
                    appConf.getNoReplyEmail(),
                    appConf.getEmailPersonalFrom(), user.getEmail(),
                    messages.getLabel("sign_up_mail_validation_subject", user.getFirstName() + " " + user.getLastName()),
                    messages.getLabel("sign_up_mail_validation_body", user.getFirstName(), keyCode + ""));

            iabakoMailSender.sendMail(iabakoMailHelper);
        }

        return user;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public User keyCodeVerification(User user, String value, IabakoPackage signUpPackage) throws IabakoUniqueConstraintException {
        HttpSession session = (((ServletRequestAttributes) RequestContextHolder.
                currentRequestAttributes()).getRequest()).getSession();

        Integer keyCode = (Integer) session.getAttribute(user.getEmail());
        if (value.equals(keyCode + "")) {
            if (signUpPackage == IabakoPackage.freeOneMonth) {

                session.removeAttribute(user.getEmail());

                user.setActive(true);
                user.setEmailConfirmed(true);
                userService.calculateUserCredit(false, user, IabakoPackage.freeOneMonth, 1);

                return userService.save(user);

            } else {
                UserPayPalToken userPayPalToken = new UserPayPalToken();
                userPayPalToken.setPayer(user);
                userPayPalToken.setPrincipalUser(user);
                userPayPalToken.setQuantity(1);
                userPayPalToken.setAmount(signUpPackage.getAmount());
                userPayPalToken.setIabakoPackage(signUpPackage);
                userPayPalToken.setTransactionId(new BigInteger(130, random).toString(32) + new Date().getTime());
                user.setUserPayPalToken(userPayPalToken);
                user.setEmailConfirmed(true);

                return userService.save(user);
            }

        }

        return user;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public UserPayPalToken getUserPayPalTokenPayedOrToVerify(String param) {
        return userService.getUserPayPalTokenPayedOrToVerify(param);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public UserPayPalToken getUserPayPalTokenByTxId(String transactionId) {
        UserPayPalToken token = userPayPalTokenDAO.getUserPayPalTokenByTxId(transactionId);
        token.getUserTokensTransient().addAll(token.getUserTokens());
        return token;
    }
}
